#include <errno.h>
#include <sys/epoll.h>
#include <sys/eventfd.h>
#include <unistd.h>

typedef struct ez_epoll_state_s {
    int epfd;
    int evfd;
    struct epoll_event events[0];
} ez_epoll_state;

#define CMD_STOP 0xfeef

static void do_eventfd_cmd(ez_event_loop_t *eventLoop) {
    eventfd_t cmd = 0;
    ez_epoll_state *state = eventLoop->apidata;

    int r = eventfd_read(state->evfd, &cmd);
    log_debug("epool_evfd:%d read cmd:0x%0x, result:%d", state->evfd, cmd, r);

    if (cmd == CMD_STOP) {
        log_info("epool_evfd:%d stop eventLoop!", state->evfd);
        eventLoop->stop = 1;
    }
}

int api_epoll_create(ez_event_loop_t *eventLoop) {
    ez_epoll_state *state = (ez_epoll_state *) ez_malloc( sizeof(ez_epoll_state) + sizeof(struct epoll_event) * eventLoop->size);

    if (!state)
        return AE_ERR;

    state->epfd = epoll_create1(EPOLL_CLOEXEC); /* 1024 is just a hint for the kernel */
    if (state->epfd == -1) {
        ez_free(state);
        return AE_ERR;
    }

    state->evfd = eventfd(0, EFD_NONBLOCK | EFD_CLOEXEC);
    if (state->evfd == -1) {
        ez_free(state);
        return AE_ERR;
    }

    eventLoop->apidata = state;
    return AE_OK;
}

void api_epoll_delete(ez_event_loop_t *eventLoop) {
    ez_epoll_state *state = eventLoop->apidata;

    close(state->epfd);
    close(state->evfd);

    ez_free(state);

    eventLoop->apidata = NULL;
}

int api_epoll_add_event(ez_event_loop_t *eventLoop, int fd, int mask, int oldmask) {
    ez_epoll_state *state = eventLoop->apidata;
    struct epoll_event ee;
    memset(&ee, 0, sizeof(ee));

    /* If the fd was already monitored for some event, we need a MOD
     * operation. Otherwise we need an ADD operation. */
    int op = (oldmask == AE_NONE) ? EPOLL_CTL_ADD : EPOLL_CTL_MOD;

    ee.events = 0;
    mask |= oldmask; /* Merge old events */
    if (mask & AE_READABLE)
        ee.events |= EPOLLIN;
    if (mask & AE_WRITABLE)
        ee.events |= EPOLLOUT;
    ee.data.u64 = 0; /* avoid valgrind warning */
    ee.data.fd = fd;
    if (epoll_ctl(state->epfd, op, fd, &ee) == -1)
        return -1;
    return AE_OK;
}

void api_epoll_del_event(ez_event_loop_t *eventLoop, int fd, int delmask, int oldmask) {
    ez_epoll_state *state = eventLoop->apidata;
    struct epoll_event ee;
    memset(&ee, 0, sizeof(ee));

    int mask = oldmask & (~delmask);

    ee.events = 0;
    if (mask & AE_READABLE)
        ee.events |= EPOLLIN;
    if (mask & AE_WRITABLE)
        ee.events |= EPOLLOUT;
    ee.data.u64 = 0; /* avoid valgrind warning */
    ee.data.fd = fd;
    if (mask != AE_NONE) {
        epoll_ctl(state->epfd, EPOLL_CTL_MOD, fd, &ee);
    } else {
        /* Note, Kernel < 2.6.9 requires a non null event pointer even for
         * EPOLL_CTL_DEL. */
        epoll_ctl(state->epfd, EPOLL_CTL_DEL, fd, &ee);
    }
}

void api_epoll_stop(ez_event_loop_t *eventLoop) {
    ez_epoll_state *state = eventLoop->apidata;
    int r = eventfd_write(state->evfd, CMD_STOP);
    log_debug("epool_evfd:%d cmd:0x%0x, r:%d", state->evfd, CMD_STOP, r);
}

void api_epoll_before(ez_event_loop_t *eventLoop) {
    ez_epoll_state *state = eventLoop->apidata;
    api_epoll_add_event(eventLoop, state->evfd, AE_READABLE, AE_NONE);
}

void api_epoll_after(ez_event_loop_t *eventLoop) {
    ez_epoll_state *state = eventLoop->apidata;
    api_epoll_del_event(eventLoop, state->evfd, AE_READABLE, AE_READABLE);
}

int api_epoll_poll(ez_event_loop_t *eventLoop, int timeout) {
    ez_epoll_state *state = eventLoop->apidata;
    int retval, numevents = 0;
    int err;
    if (timeout < 0)
        timeout = -1;
    // timeout: 超时时间（毫秒）：
    // -1：无限阻塞，直到事件发生
    //  0：立即返回，即使无事件
    // >0：等待指定时长后超时返回
    do {
        retval = epoll_wait(state->epfd, state->events, eventLoop->size, timeout);
        // was interrupted try again.
    } while (retval == -1 && ((err = errno) == EINTR));

    if (retval > 0) {

        numevents = 0;

        for (int j = 0; j < retval; j++) {
            struct epoll_event *e = &state->events[j];

            // 优先处理 evfd 的事件
            if (e->data.fd == state->evfd) {
                do_eventfd_cmd(eventLoop);
                continue;
            }

            int what = e->events;

            /* nginx 处理方式 */
            if ((what & (EPOLLERR | EPOLLHUP)) && (what & (EPOLLIN | EPOLLOUT)) == 0) {
                /*
                 * if the error events were returned without EPOLLIN or EPOLLOUT,
                 * then add these flags to handle the events at least in one
                 * active handler
                 */
                what |= EPOLLIN | EPOLLOUT;
            }

            int mask = 0;
            if (what & EPOLLIN)
                mask |= AE_READABLE;
            if (what & EPOLLOUT)
                mask |= AE_WRITABLE;

            if (mask) {
                eventLoop->fired[numevents].fd = e->data.fd;
                eventLoop->fired[numevents].mask = mask;
                ++numevents;
            }
        }
    }
    return numevents;
}

static ez_event_api_t module = {
        .name = "epoll",
        .create = api_epoll_create,
        .delete = api_epoll_delete,

        .before = api_epoll_before,
        .do_poll = api_epoll_poll,
        .after = api_epoll_after,
        .stop_poll = api_epoll_stop,

        .add_event = api_epoll_add_event,
        .del_event = api_epoll_del_event
};
